/**
 * 
 * Copyright (C) 2011 Cody Stoutenburg . All rights reserved.
 *
 *       This program is free software; you can redistribute it and/or
 *       modify it under the terms of the GNU Lesser General Public License
 *       as published by the Free Software Foundation; either version 2.1
 *       of the License, or (at your option) any later version.
 *
 *       This program is distributed in the hope that it will be useful,
 *       but WITHOUT ANY WARRANTY; without even the implied warranty of
 *       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *       GNU Lesser General Public License for more details.
 *
 *       You should have received a copy of the GNU Lesser General Public License
 *       along with this program; if not, write to the Free Software
 *       Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. 
 * 
 */
package ca.usherbrooke.model.entity.behaviours;

import java.util.ArrayList;
import java.util.List;

import org.newdawn.slick.geom.Shape;

import ca.usherbrooke.model.entity.IEntityModel;
import ca.usherbrooke.model.equipment.EquipmentModel;
import ca.usherbrooke.model.equipment.ProtectionModel;
import ca.usherbrooke.model.equipment.WeaponModel;
import ca.usherbrooke.tools.Randomizer;

/**
 * @author Cody Stoutenburg
 * 
 */
public class FightBehaviour extends EntityBehaviour {
	private static final long serialVersionUID = 1L;

	private static Boolean containEquipment(IEntityModel entity,
			EquipmentModel searchFor) {
		Boolean contain = false;
		for (EquipmentModel equipment : entity.getEquipments()) {
			if (equipment.equals(searchFor)) {
				contain = true;
				break;
			}
		}

		return contain;
	}

	public static Boolean canWearProtection(IEntityModel entity,
			ProtectionModel newProtection) {
		Boolean canWear = containEquipment(entity, newProtection);

		if (canWear) {
			for (ProtectionModel protection : entity.getCurrentProtection()) {
				if (protection.getLocationType().equals(
						newProtection.getLocationType())) {
					canWear = false;
					break;
				}
			}
		}

		return canWear;
	}

	public static Boolean doesWearProtection(IEntityModel entity,
			ProtectionModel protection) {
		Boolean doesWear = false;
		for (ProtectionModel wear : entity.getCurrentProtection()) {
			if (wear.equals(protection)) {
				doesWear = true;
				break;
			}
		}
		return doesWear;
	}

	public static Boolean canTakeWeapon(IEntityModel entity, WeaponModel weapon) {
		Boolean canTake = entity.getCurrentWeapon() == null;

		if (canTake) {
			canTake = containEquipment(entity, weapon);
		}

		return canTake;
	}

	// Health Point information
	private final Integer maxHp;
	private Integer currentHp;

	// Fight information
	private final int attackReloadTimeMs;
	private int remainingAttackReloadTimeMs;
	private final Integer currentAttackModifier;
	private final Integer damageAdd;

	private WeaponModel currentWeapon;
	private final List<ProtectionModel> currentProtections;

	/**
	 * @param model
	 * @param attackReloadTimeMs
	 * @param remainingAttackReloadTimeMs
	 * @param currentProtection
	 * @param currentAttackModifier
	 * @param damageAdd
	 * @param currentWeapon
	 * @param defaultWeapon
	 */
	public FightBehaviour(IEntityModel model, Integer maxHp, Integer currentHp,
			int attackReloadTimeMs, Integer currentAttackModifier,
			Integer damageAdd) {
		super(model);
		this.maxHp = maxHp;
		this.currentHp = currentHp;

		this.attackReloadTimeMs = attackReloadTimeMs;
		this.remainingAttackReloadTimeMs = 0;
		this.currentAttackModifier = currentAttackModifier;
		this.damageAdd = damageAdd;

		this.currentWeapon = null;

		currentProtections = new ArrayList<ProtectionModel>();
	}

	public void takeProtection(ProtectionModel protection) {
		if (canWearProtection(this.entity, protection)) {
			entity.putDown(protection);
			protection.setHolder(this.entity);
			currentProtections.add(protection);
		}
	}

	public void putDownProtection(ProtectionModel protection) {
		if (doesWearProtection(this.entity, protection)) {
			protection.setHolder(null);
			protection.setPosition(entity.getPosition());
			if (entity.canPickUpEquipment(protection)) {
				entity.pickUp(protection);
				currentProtections.remove(protection);
			} else {
				protection.setHolder(this.entity);
			}
		}
	}

	public void takeWeapon(WeaponModel weapon) {
		if (canTakeWeapon(this.entity, weapon)) {
			entity.putDown(weapon);
			weapon.setHolder(this.entity);
			currentWeapon = weapon;
		}
	}

	public void putDownWeapon() {
		if (currentWeapon != null) {
			currentWeapon.setHolder(null);
			currentWeapon.setPosition(entity.getPosition());
			if (entity.canPickUpEquipment(currentWeapon)) {
				entity.pickUp(currentWeapon);
				currentWeapon = null;
			} else {
				currentWeapon.setHolder(this.entity);
			}
		}
	}

	public void doAttack(IEntityModel defender) {
		// verify if the entity can attack
		if (remainingAttackReloadTimeMs == 0 && currentWeapon != null) {
			remainingAttackReloadTimeMs = attackReloadTimeMs;
			// verify range
			if (getDistance(this.entity.getSize(), defender.getSize()) <= getRange()) {
				Integer attack = Randomizer.dice(20) + getAttackModifier();
				// do an attack
				if (attack > defender.getDefense()) {
					Integer degat = generateDamage();
					defender.doDamage(degat);
				}
			}
		}
	}

	public void doDamage(int damage) {
		if (damage < 0) {
			damage = 0;
		}
		this.currentHp -= damage;
	}

	public void doHeal(int healValue) {
		if (healValue < 0) {
			healValue = 0;
		}
		this.currentHp += healValue;

		this.currentHp = Math.max(this.currentHp, this.maxHp);
	}

	public Boolean isDead() {
		return currentHp < 0;
	}

	public void doLogic(int delta) {
		if (remainingAttackReloadTimeMs > 0) {
			remainingAttackReloadTimeMs -= delta;
			if (remainingAttackReloadTimeMs < 0) {
				remainingAttackReloadTimeMs = 0;
			}
		}
	}

	// / Getters

	public WeaponModel getCurrentWeapon() {
		return this.currentWeapon;
	}

	public List<ProtectionModel> getCurrentProtection() {
		return this.currentProtections;
	}

	public Integer getMaxHP() {
		return this.maxHp;
	}

	public Integer getCurrentHP() {
		return this.currentHp;
	}

	public Float getRange() {
		Float range = 0.0f;
		if (currentWeapon != null) {
			range = this.currentWeapon.getRange();
		}
		return range;
	}

	public Integer getAttackModifier() {
		return this.currentAttackModifier;
	}

	public Integer getDefense() {
		Integer total = 10;
		for (ProtectionModel defense : this.currentProtections) {
			total += defense.getBonusDefense();
		}
		return total;
	}

	public Integer getRemainingAttackTime() {
		return remainingAttackReloadTimeMs;
	}

	// //// ATTACK METHODS //////
	private Integer generateDamage() {
		Integer damage = damageAdd;
		if (currentWeapon != null) {
			damage += this.currentWeapon.getDamageValue();
		}
		return damage;
	}

	private static Double getDistance(Shape shape1, Shape shape2) {
		Double result = Math.pow(shape1.getCenterX() - shape2.getCenterX(), 2)
				+ Math.pow(shape1.getCenterY() - shape2.getCenterY(), 2);

		return Math.sqrt(result);
	}
}
